package com.tencent.sr.iris.activity.service.activity.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.tencent.sr.iris.activity.common.enums.ActivityPeriodTypeEnum;
import com.tencent.sr.iris.activity.common.enums.TaskEstimateStatusEnum;
import com.tencent.sr.iris.activity.common.enums.TaskRecordAwardIssueStatusEnum;
import com.tencent.sr.iris.activity.domain.event.dto.EsQueryDTO;
import com.tencent.sr.iris.activity.interfaces.dto.ActivityInfoDTO;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityIdEnum;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityRecordStatusEnum;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityTypeEnum;
import com.tencent.sr.iris.activity.interfaces.enums.IsParticipatedEnum;
import com.tencent.sr.iris.activity.interfaces.request.acticity.GetJuniorTaskRecordListRequest;
import com.tencent.sr.iris.activity.repository.Criteria;
import com.tencent.sr.iris.activity.repository.entity.TIrisUserActivityRecordDO;
import com.tencent.sr.iris.activity.repository.entity.TIrisUserTaskRecordDO;
import com.tencent.sr.iris.activity.repository.singlemapper.TIrisUserActivityRecordMapper;
import com.tencent.sr.iris.activity.repository.singlemapper.example.TIrisUserActivityRecordExample;
import com.tencent.sr.iris.activity.service.activity.*;
import com.tencent.sr.iris.activity.service.config.ActivityConfig;
import com.tencent.sr.iris.activity.service.constant.ActivityConfigConstants;
import com.tencent.sr.iris.activity.service.convert.UserActivityRecordConvert;
import com.tencent.sr.iris.activity.service.event.util.ActivityCommonUtilService;
import com.tencent.sr.iris.activity.service.util.ESUtils;
import com.tencent.sr.iris.activity.service.util.IdCreateorUtil;
import com.tencent.sr.rmall.common.request.PaginationRequest;
import com.tencent.sr.rmall.common.response.PaginationResponse;
import com.tencent.sr.rmall.material.interfaces.enums.EYesNo;
import com.tencent.sr.rmall.springbootstarter.redis.RedisClient;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.hutool.core.util.StrUtil.toCamelCase;

@Slf4j
@Service
public class IrisUserActivityRecordServiceImpl extends EsDataOperateService implements IrisUserActivityRecordService {

    @Value("${env.es.activity.record.index.name:user-activity-record}")
    protected String indexName;

    @Override
    protected String[] indices() {
        return new String[]{indexName};
    }

    @Resource
    private TIrisUserActivityRecordMapper activityRecordMapper;

    @Resource
    private IrisUserTaskRecordService userTaskRecordService;

    @Resource
    private IrisActivityTaskService activityTaskService;

    @Resource
    private ActivityCommonService activityCommonService;

    @Resource
    private ActivityCommonUtilService activityCommonUtilService;

    @Resource
    private ActivityConfig activityConfig;

    @Resource
    private UserActivityRecordConvert activityRecordConvert;

    @Resource
    private IdCreateorUtil idCreateorUtil;

    @Resource
    private Map<String, ActivityRecordQueryHandler> activityRecordQueryHandlerMap;

    @Resource
    private RedisClient redisClient;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public void addOrUpdateActivityRecord(String uid, Long eventTime, String activityId,
                                          int periodType, String actPeriod) {
        String period = StrUtil.EMPTY;
        if (!Objects.equals(activityId, ActivityIdEnum.REGIST_GIFT.getCode())
                && periodType == ActivityPeriodTypeEnum.PERIOD.getCode()) {
            LocalDateTime localDateTime = LocalDateTime
                    .ofInstant(Instant.ofEpochMilli(eventTime), ZoneId.systemDefault());
            period = localDateTime.format(DateTimeFormatter.ofPattern(actPeriod));
        }

        List<TIrisUserTaskRecordDO> taskRecords = userTaskRecordService.queryActivityTaskRecords(uid, activityId, period);
        if (CollectionUtil.isEmpty(taskRecords)) {
            //无任务记录需删除活动记录
            deleteActivityRecord(uid, activityId, period);
            return;
        }
        if (Objects.equals(activityId, ActivityIdEnum.LEVEL_UP_PROMO_GIFT.getCode())) {
            for (TIrisUserTaskRecordDO record : taskRecords) {
                doAddOrUpdateActivityRecord(uid, activityId, period, Arrays.asList(record));
            }
        } else {
            doAddOrUpdateActivityRecord(uid, activityId, period, taskRecords);
        }
    }

    @Override
    public void doAddOrUpdateActivityRecord(String uid, String activityId, String period, List<TIrisUserTaskRecordDO> taskRecords) {
        TIrisUserTaskRecordDO last = taskRecords.get(taskRecords.size() - 1);
        String taskId = null;
        if (Objects.equals(activityId, ActivityIdEnum.LEVEL_UP_PROMO_GIFT.getCode())) {
            taskId = last.getTaskId();
        }
        if (Objects.equals(activityId, ActivityIdEnum.REGIST_GIFT.getCode())) {
            period = StrUtil.EMPTY;
        }

        TIrisUserActivityRecordDO activityRecord = queryActivityRecord(uid, activityId, period, taskId);
        boolean newActivityRecord = activityRecord == null;

        ActivityInfoDTO activityInfo = newActivityInfo(taskRecords, newActivityRecord);

        Date now = new Date();
        if (newActivityRecord) {
            activityRecord = activityRecordConvert.buildActivityRecord(last);

            activityRecord.setCreateTime(now);
            activityRecord.setBizId(idCreateorUtil.genId());
            activityRecord.setActivityPeriod(period);
            activityRecord.setTotalTier(activityInfo.getTotalTier());
            activityRecord.setInviteTime(activityInfo.getInviteTime());
            if (Objects.equals(activityId, ActivityIdEnum.LEVEL_UP_PROMO_GIFT.getCode())) {
                activityRecord.setActivityId(new StringBuilder(activityId).append("@")
                        .append(taskId).toString());
            }
        }
        activityRecord.setIsParticipated(activityInfo.getIsParticipated());
        activityRecord.setOrderFinishTime(activityInfo.getOrderFinishTime());
        activityRecord.setCurrentActivityStartTime(activityInfo.getCurrentActivityStartTime());
        activityRecord.setCurrentActivityEndTime(activityInfo.getCurrentActivityEndTime());
        activityRecord.setAwardAmount(activityInfo.getAwardAmount());
        activityRecord.setEstimateAwardAmount(activityInfo.getEstimateAwardAmount());
        activityRecord.setCurrentTier(activityInfo.getCurrentTier());
        activityRecord.setActivityStatus((byte) activityInfo.getActivityStatus().getCode());
        activityRecord.setUpdateTime(now);
        activityRecord.setStatus(EYesNo.NO.getValue());
        //指标设置
        Integer gmv = taskRecords.stream().filter(i -> i.getGmv() != null)
                .map(TIrisUserTaskRecordDO::getGmv).max(Integer::compareTo).orElse(0);
        activityRecord.setGmv(gmv);

        Integer shareCount = taskRecords.stream().filter(i -> i.getShareCount() != null)
                .map(TIrisUserTaskRecordDO::getShareCount).max(Integer::compareTo).orElse(0);
        activityRecord.setShareCount(shareCount);

        Integer commission = taskRecords.stream().filter(i -> i.getCommission() != null)
                .map(TIrisUserTaskRecordDO::getCommission).max(Integer::compareTo).orElse(0);
        activityRecord.setCommission(commission);

        Integer estimateCommission = taskRecords.stream().filter(i -> i.getEstimateCommission() != null)
                .map(TIrisUserTaskRecordDO::getEstimateCommission).max(Integer::compareTo).orElse(0);
        activityRecord.setEstimateCommission(estimateCommission);

        activityRecordMapper.batchAddOrUpdate(Arrays.asList(activityRecord));
        if (newActivityRecord || activityConfig.isAlwaysUpdateActivityRecordId()) {
            userTaskRecordService.updateTaskRecordActivityRecordId(uid, activityId, period, taskId, activityRecord.getBizId());
        }
    }

    /**
     * 处理任务状态
     *
     * @param taskRecords
     * @param newActivityRecord
     * @return
     */
    @Override
    public ActivityInfoDTO newActivityInfo(List<TIrisUserTaskRecordDO> taskRecords, boolean newActivityRecord) {
        if (CollectionUtil.isEmpty(taskRecords)) {
            return null;
        }
        int totalStage = taskRecords.size();
        TIrisUserTaskRecordDO last = taskRecords.get(totalStage - 1);
        String uid = last.getUid();

        int commissionPlusDays = activityConfig.getCommissionPlusDays(uid);
        ActivityInfoDTO activity = activityCommonUtilService.handleActivityStatus(taskRecords, commissionPlusDays);
        activity.setCurrentActivityStartTime(last.getCurrentTaskStartTime())
                .setCurrentActivityEndTime(last.getCurrentTaskEndTime())
                .setTotalTier(taskRecords.size())
                .setAwardAmount(0);
        List<TIrisUserTaskRecordDO> issueSuccessList = taskRecords.stream()
                .filter(i -> Objects.equals(i.getAwardIssueStatus(), TaskRecordAwardIssueStatusEnum.ISSUE_SUCCEED.getCode())
                        || Objects.equals(i.getAwardIssueStatus(), TaskRecordAwardIssueStatusEnum.ISSUE_FAILED.getCode()))
                .collect(Collectors.toList());
        Integer awardInfo = getAwardInfo(issueSuccessList);
        activity.setAwardAmount(awardInfo);

        List<TIrisUserTaskRecordDO> estimateList = taskRecords.stream()
                .filter(i -> Objects.equals(i.getEstimateUserTaskStatus(), TaskEstimateStatusEnum.FINISHED.getCode()))
                .collect(Collectors.toList());
        Integer estimateAwardAmount = getAwardInfo(estimateList);
        activity.setEstimateAwardAmount(estimateAwardAmount);

        if (newActivityRecord) {
            Long changeTime = activityCommonService.queryTraineeTime(uid);
            activity.setInviteTime(Optional.ofNullable(changeTime).map(DateUtil::date).orElse(null));
        }
        activity.setOrderFinishTime(Optional.ofNullable(last).map(TIrisUserTaskRecordDO::getOrderFinishTime).orElse(null));

        boolean limited = taskRecords.stream().anyMatch(i -> Objects.equals(i.getIsParticipated(), IsParticipatedEnum.LIMITED.name()));
        activity.setIsParticipated(limited ? IsParticipatedEnum.LIMITED.name() : IsParticipatedEnum.YES.name());

        return activity;
    }

    /**
     * 计算奖励金
     * @param list
     * @return
     */
    private Integer getAwardInfo(List<TIrisUserTaskRecordDO> list) {
        if (CollectionUtil.isNotEmpty(list)) {
            return list.stream().filter(i -> i.getSendAward() != null)
                    .mapToInt(TIrisUserTaskRecordDO::getSendAward).sum();
        }
        return 0;
    }

    @Override
    public TIrisUserActivityRecordDO queryActivityRecord(String uid, String activityId, String period, String taskId) {
        if (Objects.equals(activityId, ActivityIdEnum.REGIST_GIFT.getCode())) {
            period = StrUtil.EMPTY;
        }
        if (StrUtil.isNotBlank(taskId)) {
            activityId = new StringBuilder(activityId).append("@")
                    .append(taskId).toString();
        }
        TIrisUserActivityRecordExample example = new TIrisUserActivityRecordExample();
        Criteria criteria = example.createCriteria()
                .andPropertyEqualTo(TIrisUserActivityRecordExample.UID, uid)
                .andPropertyEqualTo(TIrisUserActivityRecordExample.ACTIVITY_ID, activityId);
        if (period != null) {
            criteria.andPropertyEqualTo(TIrisUserActivityRecordExample.ACTIVITY_PERIOD, period);
        }

        List<TIrisUserActivityRecordDO> list = activityRecordMapper.selectByExample(example);
        if (CollectionUtil.isNotEmpty(list)) {
            return list.get(0);
        }

        return null;
    }

    @Override
    public int deleteActivityRecord(String uid, String activityId, String period) {
        if (Objects.equals(activityId, ActivityIdEnum.REGIST_GIFT.getCode())) {
            period = StrUtil.EMPTY;
        }
        TIrisUserActivityRecordExample example = new TIrisUserActivityRecordExample();
        Criteria criteria = example.createCriteria().andPropertyEqualTo(TIrisUserActivityRecordExample.STATUS, EYesNo.NO.getValue())
                .andPropertyEqualTo(TIrisUserActivityRecordExample.UID, uid)
                .andPropertyEqualTo(TIrisUserActivityRecordExample.ACTIVITY_ID, activityId);
        if (period != null) {
            criteria.andPropertyEqualTo(TIrisUserActivityRecordExample.ACTIVITY_PERIOD, period);
        }

        TIrisUserActivityRecordDO update = new TIrisUserActivityRecordDO();
        update.setStatus(EYesNo.YES.getValue());
        update.setUpdateTime(new Date());
        int i = activityRecordMapper.updateByExampleSelective(update, example);
        log.info("删除{}条活动记录，uid：{}，activityId：{}，period：{}", i, uid, activityId, period);
        return i;
    }

    @Override
    public int handleInProgressActivityRecord(String uid, String activityId) {
        TIrisUserActivityRecordExample example = new TIrisUserActivityRecordExample();
        example.createCriteria().andPropertyEqualTo(TIrisUserActivityRecordExample.STATUS, EYesNo.NO.getValue())
                .andPropertyEqualTo(TIrisUserActivityRecordExample.UID, uid)
                .andPropertyEqualTo(TIrisUserActivityRecordExample.ACTIVITY_ID, activityId)
                .andPropertyNotEqualTo(TIrisUserActivityRecordExample.ACTIVITY_STATUS, ActivityRecordStatusEnum.FINISHED.getCode())
                .andPropertyNotEqualTo(TIrisUserActivityRecordExample.ACTIVITY_STATUS, ActivityRecordStatusEnum.UNFINISHED.getCode());

        List<TIrisUserActivityRecordDO> list = activityRecordMapper.selectByExample(example);
        log.info("重新处理{}条活动记录，uid：{}， activityId：{}", list.size(), uid, activityId);
        for (TIrisUserActivityRecordDO record : list) {
            addOrUpdateActivityRecord(uid, record.getCurrentActivityStartTime().getTime(),
                    activityId, record.getPeriodType(), record.getActivityPeriod());
        }
        return list.size();
    }

    @Override
    public PaginationResponse<TIrisUserActivityRecordDO> queryActivityRecordFromEs(PaginationRequest<GetJuniorTaskRecordListRequest> request) {
        GetJuniorTaskRecordListRequest queryCriteria = request.getQueryCriteria();
        BoolQueryBuilder query = getQueryActivityRecordFromEsQuery(queryCriteria);

        // 处理排序： 默认根据被邀请时间进行排序，否则根据前端传入排序
        FieldSortBuilder sortBuilder = SortBuilders.fieldSort(toCamelCase(TIrisUserActivityRecordExample.INVITE_TIME))
                .order(SortOrder.DESC);
        if (StringUtils.hasText(queryCriteria.getOrder())) {
            sortBuilder = SortBuilders.fieldSort(toCamelCase(queryCriteria.getOrder()))
                    .order(queryCriteria.getDesc() ? SortOrder.DESC : SortOrder.ASC);
        }

        EsQueryDTO esQueryDTO = EsQueryDTO.builder().query(query).routing("0")
                .pageNum(request.getPageNum())
                .pageSize(request.getPageSize())
                .sortBuilder(sortBuilder).build();

        Function<SearchResponse, List<TIrisUserActivityRecordDO>> func = (r) -> ESUtils
                .buildResponse(r, TIrisUserActivityRecordDO.class);
        return searchPage(esQueryDTO, func);
    }

    @Override
    public boolean existActivityRecord(String uid, String activityId, String period) {
        TIrisUserActivityRecordExample example = new TIrisUserActivityRecordExample();
        example.createCriteria().andPropertyEqualTo(TIrisUserActivityRecordExample.STATUS, EYesNo.NO.getValue())
                .andPropertyEqualTo(TIrisUserActivityRecordExample.UID, uid)
                .andPropertyEqualTo(TIrisUserActivityRecordExample.ACTIVITY_ID, activityId);

        if (period != null) {
            example.createCriteria().andPropertyEqualTo(TIrisUserActivityRecordExample.ACTIVITY_PERIOD, period);
        }

        return CollectionUtil.isNotEmpty(activityRecordMapper.limitSelectByExample(example, 0, 1));
    }

    /**
     * 获取查询活动记录查询对象
     *
     * @param queryCriteria queryCriteria
     * @return BoolQueryBuilder
     */
    private BoolQueryBuilder getQueryActivityRecordFromEsQuery(GetJuniorTaskRecordListRequest queryCriteria) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery(toCamelCase(TIrisUserActivityRecordExample.PID), queryCriteria.getPid()))
                .must(QueryBuilders.termQuery(toCamelCase(TIrisUserActivityRecordExample.STATUS), EYesNo.NO.getValue()));

        // 活动id过滤
        List<String> activityIds = queryCriteria.getActivityIds();
        if (CollectionUtil.isNotEmpty(activityIds)) {
            query.must(QueryBuilders.termsQuery(toCamelCase(TIrisUserActivityRecordExample.ACTIVITY_ID), activityIds));
        }

        Long startTime = queryCriteria.getStartTime();
        if(Objects.nonNull(startTime)){
            query.must(QueryBuilders.rangeQuery(toCamelCase(
                            TIrisUserActivityRecordExample.CURRENT_ACTIVITY_START_TIME)).gte(startTime));
        }

        Long endTime = queryCriteria.getEndTime();
        if(Objects.nonNull(endTime)){
            query.must(QueryBuilders.rangeQuery(toCamelCase(
                    TIrisUserActivityRecordExample.CURRENT_ACTIVITY_START_TIME)).lte(endTime));
        }

        // 状态字段过滤处理
        List<Integer> statusList = queryCriteria.getStatusList();
        if (CollectionUtil.isNotEmpty(statusList)) {
            Integer activityStatus = statusList.get(0);
            String handlerKey = String.format("%s_QUERY_HANDLER", activityIds.get(0));
            ActivityRecordQueryHandler activityRecordQueryHandler = activityRecordQueryHandlerMap.get(handlerKey);
            activityRecordQueryHandler.statusFilterWrap(query, activityStatus);
        }

        if (StringUtils.hasText(queryCriteria.getUid())) {
            query.must(QueryBuilders.termQuery(toCamelCase(TIrisUserActivityRecordExample.UID), queryCriteria.getUid()));
        }

        return query;
    }

    @Override
    public Integer queryAwardTotalByPid(String pid, Date date) {
        String inviteAwardLimitCacheKey = String.format(ActivityConfigConstants.IRIS_INVITE_UPPER_RECORD_AWARD_LIMIT_CACHE_KEY, pid,
                DateUtil.format(date, "yyyyMM"));
        String s = redisClient.get(inviteAwardLimitCacheKey);
        if (StrUtil.isNotBlank(s)) {
            log.info("IrisUserActivityRecordServiceImpl.queryAwardTotalByPid redis ,pid:{},value:{}", pid, s);
            return Integer.parseInt(s);
        }

        DateTime begin = DateUtil.beginOfMonth(date);
        DateTime end = DateUtil.endOfMonth(date);
        TIrisUserActivityRecordExample example = new TIrisUserActivityRecordExample();
        example.setDistinct(true);
        example.setCountField(TIrisUserActivityRecordExample.UID);
        example.createCriteria()
                .andPropertyBetween(TIrisUserActivityRecordExample.CURRENT_ACTIVITY_START_TIME, begin, end)
                .andPropertyEqualTo(TIrisUserActivityRecordExample.ACTIVITY_TYPE, ActivityTypeEnum.INVITE_GIFT.getCode())
                .andPropertyEqualTo(TIrisUserActivityRecordExample.ACTIVITY_ID, ActivityIdEnum.INVITE_GIFT.getCode())
                .andPropertyEqualTo(TIrisUserActivityRecordExample.PID, pid)
                .andPropertyNotEqualTo(TIrisUserActivityRecordExample.IS_PARTICIPATED, IsParticipatedEnum.LIMITED.name())
                .andPropertyGreaterThan(TIrisUserActivityRecordExample.AWARD_AMOUNT, 0);
        long count = activityRecordMapper.countFieldByExample(example);
        redisClient.set(inviteAwardLimitCacheKey, String.valueOf(count));
        return Math.toIntExact(count);
    }
}
